export class Multiset<T> {
    data: Map<T, number> = null;
    size: number = 0;

    constructor() {
        this.data = new Map<T, number>();
        this.size = 0;
    }

    addBySet(set: Set<T>) {
        set.forEach(_ => {
            this.add(_);
        });
        return this;
    }

    addByList(list: Array<T>) {
        list.forEach(_ => {
            this.add(_);
        });
        return this;
    }

    add(key: T, count = 1) {
        if (!this.data.has(key)) {
            this.data.set(key, 0);
        }
        this.data.set(key, this.data.get(key) + count);
        this.size += count;
        return count;
    }

    remove(key: T, count = 1) {
        if (!this.data.has(key)) {
            this.data.set(key, 0);
        }
        if (this.data.get(key) - count < 0) {
            count = this.data.get(key);
        }
        this.data.set(key, this.data.get(key) - count);
        this.size -= count;
        return count;
    }

    delete(key: T) {
        this.remove(key);
    }

    has(key: T) {
        if (this.data.has(key))
            return this.data.get(key) > 0;
        return false;
    }

    clear() {
        this.data.clear();
        this.size = 0;
    }

    forEach(callbackfn: (value: T) => void, thisArg?: any) {
        this.data.forEach((value, key) => {
            for (let i = 0; i < value; i++) {
                callbackfn.call(thisArg, key);
            }
        }, thisArg);
    }

    toArray() {
        let a = [];
        this.forEach(element => {
            a.push(element);
        });
        return a;
    }

}